package de.tuberlin.dima.aim3.oc.input.custom;

import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.Characters;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;

import de.tuberlin.dima.aim3.oc.input.WikiDumpParser;
import de.tuberlin.dima.aim3.oc.input.jaxb.WikiDumpStaxJaxbParser;

/**
 * XML parser based on the StAX API which parses Wikipedia XML dumps either
 * iteratively or into a full list of all contained pages.
 * 
 * @see WikiDumpStaxJaxbParser WikiDumpStaxJaxbParser as an enhanced and more
 *      leightweight parser.
 * 
 * @author Florian Feigenbutz <florian.feigenbutz@campus.tu-berlin.de>
 * 
 */
public class WikiDumpStaxParser extends WikiDumpParser {

  private enum NodeType {
    IRRELEVANT, PAGE, TITLE, ID, REVISION, DATE, TIMESTAMP, CONTRIBUTOR, IP, USERNAME, MINOR, COMMENT, TEXT;

    public String getName() {
      return toString().toLowerCase();
    }
  }

  static final String DATE = "date";
  static final String PAGE = "page";
  static final String TITLE = "title";
  static final String ID = "id";
  static final String REVISION = "revision";
  static final String TIMESTAMP = "timestamp";
  static final String CONTRIBUTOR = "contributor";
  static final String IP = "ip";
  static final String USERNAME = "username";
  static final String MINOR = "minor";
  static final String COMMENT = "comment";
  static final String TEXT = "text";

  public List<WikiPage> readWikiDump(String wikiDumpFile) {

    List<WikiPage> pages = new ArrayList<WikiPage>();
    try {
      InputStream in = new FileInputStream(wikiDumpFile);

    } catch (FileNotFoundException e) {
      e.printStackTrace();
    }
    return pages;
  }

  /*
   * (non-Javadoc)
   * 
   * @see de.tuberlin.dima.aim3.oc.input.WikiDumpParser#readWikiDumpPage(byte[])
   */
  @Override
  public WikiPage readWikiDumpPage(byte[] pageAsBytes) {
    try {
      InputStream in = new ByteArrayInputStream(pageAsBytes);
      XMLInputFactory factory = XMLInputFactory.newInstance();
      XMLEventReader parser = factory.createXMLEventReader(in);

      WikiPage page = null;
      WikiRevision revision = null;
      WikiUser user = null;

      NodeType nodeType = NodeType.IRRELEVANT;
      while (parser.hasNext()) {
        XMLEvent event = parser.nextEvent();
        int eventType = event.getEventType();
        switch (eventType) {
        case XMLStreamConstants.END_DOCUMENT:
          parser.close();
          break;
        case XMLStreamConstants.START_ELEMENT:
          StartElement startElement = event.asStartElement();
          if (nodeEquals(NodeType.PAGE, startElement)) {
            if (page != null) {
              throw new IllegalStateException(
                  "Cannot create new element 'page' because last element has not been finished parsing. Please validate your XML input");
            }
            page = new WikiPage();
          }

          if (nodeEquals(NodeType.TITLE, startElement)) {
            nodeType = NodeType.TITLE;
            page.title = "";
          }

          else if (nodeEquals(NodeType.ID, startElement)) {
            nodeType = NodeType.ID;
            if (user != null) {
              user.userid = "";
            } else if (revision != null) {
              revision.id = "";
            } else {
              page.id = "";
            }
          }

          else if (nodeEquals(NodeType.REVISION, startElement)) {
            nodeType = NodeType.REVISION;
            if (revision != null) {
              throw new IllegalStateException(
                  "Cannot create new element 'revision' because last element has not been finished parsing. Please validate your XML input");
            }
            revision = new WikiRevision();
          }

          else if (nodeEquals(NodeType.TIMESTAMP, startElement)) {
            nodeType = NodeType.TIMESTAMP;
            revision.timestamp = "";
          }

          else if (nodeEquals(NodeType.CONTRIBUTOR, startElement)) {
            if (user != null) {
              throw new IllegalStateException(
                  "Cannot create new element 'user' because last element has not been finished parsing. Please validate your XML input");
            }
            user = new WikiUser();
          }

          else if (nodeEquals(NodeType.TIMESTAMP, startElement)) {
            nodeType = NodeType.TIMESTAMP;
            revision.timestamp = "";
          }

          else if (nodeEquals(NodeType.USERNAME, startElement)) {
            nodeType = NodeType.USERNAME;
            user.username = "";
          }

          else if (nodeEquals(NodeType.IP, startElement)) {
            nodeType = NodeType.IP;
            user.userip = "";
          }

          else if (nodeEquals(NodeType.MINOR, startElement)) {
            nodeType = NodeType.MINOR;
            revision.minorChange = true;
          }

          else if (nodeEquals(NodeType.COMMENT, startElement)) {
            nodeType = NodeType.COMMENT;
            revision.comment = "";
          }

          else if (nodeEquals(NodeType.TEXT, startElement)) {
            nodeType = NodeType.TEXT;
            revision.text = "";
          }

          else {
            nodeType = NodeType.IRRELEVANT;
          }

          break;
        case XMLStreamConstants.CHARACTERS:
          Characters characters = event.asCharacters();
          if (!characters.isWhiteSpace()) {
            switch (nodeType) {
            case TITLE:
              page.title += characters.getData();
              break;
            case ID:
              if (user != null) {
                user.userid += characters.getData();
              } else if (revision != null) {
                revision.id += characters.getData();
              } else {
                page.id += characters.getData();
              }
              break;
            case TIMESTAMP:
              revision.timestamp += characters.getData();
              break;
            case USERNAME:
              user.username += characters.getData();
              break;
            case IP:
              user.userip += characters.getData();
              break;
            case COMMENT:
              revision.comment += characters.getData();
              break;
            case TEXT:
              revision.text += characters.getData();
              break;
            default:
              break;
            }
          }
          break;
        case XMLStreamConstants.END_ELEMENT:
          EndElement endElement = event.asEndElement();
          if (nodeEquals(NodeType.PAGE, endElement)) {
            if (page == null) {
              throw new IllegalStateException(
                  "Cannot end element 'page' because no Page instance has been created yet. Please validate your XML input");
            }
            return page;
          }
          if (nodeEquals(NodeType.REVISION, endElement)) {
            if (revision == null) {
              throw new IllegalStateException(
                  "Cannot end element 'revision' because no Revision instance has been created yet. Please validate your XML input");
            }
            page.revisions.add(revision);
            revision = null;
          }
          if (nodeEquals(NodeType.CONTRIBUTOR, endElement)) {
            if (user == null) {
              throw new IllegalStateException(
                  "Cannot end element 'contributor' because no User instance has been created yet. Please validate your XML input");
            }
            revision.user = user;
            user = null;
          }
          break;
        }
      }
      return null;
    } catch (XMLStreamException e) {
      e.printStackTrace();
      return null;
    }
  }

  @Override
  public List<WikiPage> readWikiDumpPage() throws Exception {
    // TODO Auto-generated method stub
    return null;
  }

  private boolean nodeEquals(NodeType nodeType, StartElement element) {
    return nodeType.getName().equals(element.getName().getLocalPart());
  }

  private boolean nodeEquals(NodeType nodeType, EndElement element) {
    return nodeType.getName().equals(element.getName().getLocalPart());
  }

}
